defmodule Pango.DeeprParse do
	@moduledoc false
	require Logger


	def parse_query(request_args, user, method, caller_mod) do
		repos = caller_mod.get_repos()
		default_repo = caller_mod.default_repo()
		{repo_acc, {with_res, return}, cypher_str, cypher_params, obj_keys, query_result, pg_str, {_, pg_params}} =
			Enum.reduce(
				request_args,
				{nil, {[], []}, :first, %{}, %{}, %{}, :first, {1, []}},
				# acc1: pgsql list 查询参数{with_res, return}, acc2: 图数据库的cypher语句,
				# acc3: 所有的对象的返回字段, acc4: 执行了查询的结果, acc5: pgsql info 查询字符串
				fn ({obj, %{"=>" => select} = args} = h,
				   {repo_acc, acc1, acc2, cypher_params_acc, acc3, acc4, acc5, pg_params_acc}) ->
					{pg_res_acc, pg_return_acc} = acc1
					special_mod = Map.get(caller_mod.special_object(), obj)
					if special_mod != nil do
						a = if Kernel.function_exported?(special_mod, method, 4) do
							[obj, UserLib.get_project_id(user), user, args]
						else
							[obj, UserLib.get_project_id(user), user, args, pg_params_acc]
						end
						case apply(special_mod, method, a) do
							# pgsql list 返回字符串
							{"pg", with_res, return} ->
								repo = get_repo(repos, repo_acc, obj, default_repo)
								{
									repo,
									{[with_res | pg_res_acc], [return | pg_res_acc]},
									acc2,
									cypher_params_acc,
									acc3,
									acc4,
									acc5,
									pg_params_acc
								}
							{"pg", with_res, return, pg_params_acc} ->
								repo = get_repo(repos, repo_acc, obj, default_repo)
								{
									repo,
									{[with_res | pg_res_acc], [return | pg_res_acc]},
									acc2,
									cypher_params_acc,
									acc3,
									acc4,
									acc5,
									pg_params_acc
								}
							# pgsql info 返回字符串
							{"pg", str} when is_binary(str) ->
								{repo_acc, acc1, acc2, cypher_params_acc, acc3, acc4, union(acc5, str), pg_params_acc}
							{"pg", str, pg_params_acc} when is_binary(str) ->
								{repo_acc, acc1, acc2, cypher_params_acc, acc3, acc4, union(acc5, str), pg_params_acc}
							{"cypher", v} when is_binary(v) ->
								{repo_acc, acc1, union(acc2, v), cypher_params_acc, acc3, acc4, acc5, pg_params_acc}
							%{^obj => v} ->
								v = case method == :list && v do
									%{"total_page" => _} ->
										v
									false ->
										v
									_ ->
										MapLib.fetch_key(args, ["total_page", "[]"])
										|> Map.put(Atom.to_string(method), v)
								end
								{repo_acc, acc1, acc2, cypher_params_acc, acc3, Map.put(acc4, obj, v), acc5, pg_params_acc}
							v ->
								v = case method == :list && v do
									%{"total_page" => _} ->
										v
									false ->
										v
									_ ->
										MapLib.fetch_key(args, ["total_page", "[]"])
										|> Map.put(Atom.to_string(method), v)
								end
								{repo_acc, acc1, acc2, cypher_params_acc, acc3, Map.put(acc4, obj, v), acc5, pg_params_acc}
						end
					else
						mod = caller_mod.get_db_mod(obj)
						is_pg = mod.db_type() == Pango.DbType.pg()
						query_params = if is_pg do
							pg_params_acc
						else
							cypher_params_acc
						end
						{bool, user, args, result, query_params} =
							case Map.get(mod.pipelines(), Atom.to_string(method)) do
								{m, f, a} ->
									params = if Kernel.function_exported?(m, f, 5 + length(a)) do
										[:before, user, obj, args, query_params | a]
									else
										[:before, user, obj, args | a]
									end
									case apply(m, f, params) do
										%{^obj => _} = result ->
											{true, user, args, result, query_params}
										# 根据项目id查询数据的时候可用
										{%{"project_id" => _} = user, args} ->
											{false, user, args, nil, query_params}
										{user, args, query_params} ->
											{false, user, args, nil, query_params}
										{args, query_params} ->
											{false, user, args, nil, query_params}
										args ->
											{false, user, args, nil, query_params}
									end
								_ ->
									{false, user, args, nil, query_params}
							end
						cond do
							bool ->
								{repo_acc, acc1, acc2, cypher_params_acc, acc3, Map.merge(acc4, result), acc5, pg_params_acc}
							is_pg ->
								case apply(Pango.DeeprPsql, method, [caller_mod, user, obj, args, query_params]) do
									# list 查询
									{with_res, return, pg_params_acc} ->
										repo = get_repo(repos, repo_acc, obj, default_repo)
										{
											repo,
											{[with_res | pg_res_acc], [return | pg_return_acc]},
											acc2,
											cypher_params_acc,
											acc3,
											acc4,
											acc5,
											pg_params_acc
										}
									# info 查询, 暂时不支持一次查多个对象
									{str, pg_params_acc} ->
										repo = get_repo(repos, repo_acc, obj, default_repo)
										{repo, acc1, acc2, cypher_params_acc, acc3, acc4, union(acc5, str), pg_params_acc}
								end
							true ->
								{v, params} = apply(Pango.DeeprCypherV2, method, [caller_mod, user, obj, args])
								{repo_acc, acc1, union(acc2, v), Map.merge(params, query_params), acc3, acc4, acc5, pg_params_acc}
						end
					end
				end
			)
		repo_acc = if repo_acc == nil do
			{PsqlDb, :run, []}
		else
			repo_acc
		end
		pg_params = Enum.reverse(pg_params)
		if with_res != [] do
			with_res = Enum.join(with_res, ",")
			return = Enum.join(return, ",")
			str = "WITH #{with_res} SELECT #{return}"
			{repo_acc, str, pg_params, cypher_str, cypher_params, obj_keys, query_result}
		else
			pg_str == if pg_str != :first do
				IO.iodata_to_binary(pg_str)
			else
				pg_str
			end
			{repo_acc, pg_str, pg_params, cypher_str, cypher_params, obj_keys, query_result}
		end
	end

	defp union(:first, sql) do
		sql
	end
	defp union(acc, sql) do
		[acc, " UNION ALL " | sql]
	end


	def get_repo(repos, repo_acc, obj, default_repo) do
		repo = Map.get(repos, obj, default_repo)
		repo_acc == nil || repo == repo_acc || PrivateCandy.debug_remind(__ENV__, "obj的repo不一致, #{obj}")
		repo
	end

end
